#include "MediaCtroller.h"
#include "PeerConnection.hpp"
#include "api/peer_connection_interface.h"
#include "api/media_stream_interface.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/create_peerconnection_factory.h"
#include "api/video_codecs/builtin_video_decoder_factory.h"
#include "api/video_codecs/builtin_video_encoder_factory.h"
#include "api/task_queue/default_task_queue_factory.h"
#include "modules/audio_device/include/audio_device.h"
#include "Producer.hpp"
#include "DataProducer.hpp"
#include "Consumer.hpp"
#include "DataConsumer.hpp"
#include <random>
#include <memory>
#include <unordered_map>
#include "Transport.hpp"
#include "Device.hpp"
#include "Producer.hpp"
#include "DataProducer.hpp"
#include "Consumer.hpp"
#include "DataConsumer.hpp"
#include "rtc_base/thread.h"
#include "CJson.h"
#include <QDebug>
#include "api/media_stream_interface.h"
#include "api/scoped_refptr.h"
#include "api/video/video_sink_interface.h"
#include "api/video/video_frame.h"
#include "api/video/video_source_interface.h"
#include "MainWgt.h"
#include <QThread>
namespace vi {

	uint32_t msgId()
	{
		static std::default_random_engine e;
		static std::uniform_int_distribution<uint32_t> u(std::numeric_limits<uint32_t>::min(), std::numeric_limits<uint32_t>::max());
		return u(e);
	}

	MediaCtroller::MediaCtroller(QObject* parent):QObject(parent)
	{
		_websocket = new vi::WebsocketTransport;
		_websocket->addObserverV2(this);

		_networkThread = rtc::Thread::CreateWithSocketServer();
		_signalingThread = rtc::Thread::Create();
		_workerThread = rtc::Thread::Create();

		_networkThread->SetName("network_thread", nullptr);
		_signalingThread->SetName("signaling_thread", nullptr);
		_workerThread->SetName("worker_thread", nullptr);

		if (!_networkThread->Start() || !_signalingThread->Start() || !_workerThread->Start()) {
			qDebug("thread start errored");
		}

		_adm = _workerThread->Invoke<rtc::scoped_refptr<webrtc::AudioDeviceModule>>(RTC_FROM_HERE, [this]() {
			_taskQueueFactory = webrtc::CreateDefaultTaskQueueFactory();
			return webrtc::AudioDeviceModule::Create(webrtc::AudioDeviceModule::kPlatformDefaultAudio, _taskQueueFactory.get());
			});



		this->_peerConnectionFactory = webrtc::CreatePeerConnectionFactory(this->_networkThread.get(),
			this->_workerThread.get(),
			this->_signalingThread.get(),
			_adm,
			webrtc::CreateBuiltinAudioEncoderFactory(),
			webrtc::CreateBuiltinAudioDecoderFactory(),
			webrtc::CreateBuiltinVideoEncoderFactory(),
			webrtc::CreateBuiltinVideoDecoderFactory(),
			nullptr /*audio_mixer*/,
			nullptr /*audio_processing*/);

		_peerConnectionOptions.reset(new mediasoupclient::PeerConnection::Options());
		_peerConnectionOptions->config.set_dscp(true);
		_peerConnectionOptions->factory = _peerConnectionFactory;

		_mediasoupDevice = std::make_shared<mediasoupclient::Device>();
	}

	MediaCtroller::~MediaCtroller()
	{
	}

	void MediaCtroller::regiestCallback(MediasoupEventCB* cb)
	{
		m_mediasoupEventCb = cb;
	}

	void MediaCtroller::regiestFunc(std::function<void(QString, unsigned char*, int, int)> dataFunc)
	{
		m_dataFunc = dataFunc;
	}

	void MediaCtroller::connectServer(QString url)
	{
		_websocket->connect(url.toStdString(), "protoo");
	}

	void MediaCtroller::enableVideo(bool enable)
	{
		if (!_mediasoupDevice->CanProduce("video")) {
			return;
		}

		if (!_sendTransport) {
			return;
		}

			
		if (!_capturerSource) {
			//std::unique_ptr<VcmCapturer> capturer = absl::WrapUnique(VcmCapturer::Create(1280, 720, 30, 0));
			//_capturerSource = rtc::make_ref_counted<WindowsCapturerTrackSource>(std::move(capturer), false);

			_capturerSource = WindowsCapturerTrackSource::Create(_signalingThread.get());
		}

		if (_capturerSource) {
			_capturerSource->startCapture();
			rtc::scoped_refptr<webrtc::VideoTrackInterface> track = _peerConnectionFactory->CreateVideoTrack("camera-track", _capturerSource);
			track->set_enabled(true);
			nlohmann::json codecOptions = nlohmann::json::object();
			codecOptions["videoGoogleStartBitrate"] = 1000;

			mediasoupclient::Producer* producer = _sendTransport->Produce(this,
				track,
				nullptr,
				&codecOptions,
				nullptr);
			_camProducer.reset(producer);

		}

	}



	void MediaCtroller::createNewConsumer(std::string msg)
	{
		auto notification		= nlohmann::json::parse(msg.begin(), msg.end());
		std::string kind		= notification["data"]["kind"].get<std::string>();
	
		std::string id			= notification["data"]["id"].get<std::string>();
		std::string producerId	= notification["data"]["producerId"].get<std::string>();
		std::string peerId		= notification["data"]["peerId"].get<std::string>();
		nlohmann::json appData	= notification["data"]["appData"];

		mediasoupclient::Consumer* consumer = _recvTransport->Consume(this,
			id,
			producerId,
			kind,
			&notification["data"]["rtpParameters"]
		);


		if (kind == "audio"){
	
		}
		else if (kind == "video")
		{
			if (m_mediasoupEventCb) {
				m_mediasoupEventCb->onNewPeer(peerId.c_str());
			}
			webrtc::VideoTrackInterface* vTrack = static_cast<webrtc::VideoTrackInterface*>(consumer->GetTrack());
			VRender* render = new VRender;
			render->setPeerId(peerId.c_str());
			render->regiestDataFunc(m_dataFunc);
			vTrack->AddOrUpdateSink(render, rtc::VideoSinkWants());
			m_renderList.push_back(render);
		}
		
	}


	void MediaCtroller::startMediasoup()
	{
		std::thread* th = new std::thread([=] {
			
			nlohmann::json extendedRtpCapabilities;
		
			nlohmann::json request;

			//getRouterRtpCapabilities
			{
				request["id"] = msgId();
				request["method"] = "getRouterRtpCapabilities";
				request["request"] = true;
				_promise = std::unique_ptr<std::promise<std::string> >(new std::promise<std::string>);
				_websocket->send(request.dump());
				std::string res = _promise->get_future().get();

				auto response = nlohmann::json::parse(res.begin(), res.end());

				_mediasoupDevice->Load(response["data"]);
			}

	

			//CreateSendTransport
			{
				request["id"] = msgId();
				request["method"] = "createWebRtcTransport";
				request["request"] = true;
				request["data"] = {
					{"forceTcp", false},
					{"producing", true},
					{"consuming", false},
				};
				_promise = std::unique_ptr<std::promise<std::string> >(new std::promise<std::string>);
				_websocket->send(request.dump());
				std::string res = _promise->get_future().get();

				if (!_mediasoupDevice) {
					return;
				}
				auto response = nlohmann::json::parse(res.begin(), res.end());

				RTC_LOG(LS_INFO) << "wss recv " << res;
				std::string transportId = response["data"]["id"].get<std::string>();
				nlohmann::json iceParameters = response["data"]["iceParameters"];
				nlohmann::json iceCandidates = response["data"]["iceCandidates"];
				nlohmann::json dtlsParameters = response["data"]["dtlsParameters"];
				nlohmann::json sctpParameters = response["data"]["sctpParameters"];

				auto sendTransport = _mediasoupDevice->CreateSendTransport(this, transportId.c_str(), iceParameters, iceCandidates, dtlsParameters, sctpParameters, _peerConnectionOptions.get());
				_sendTransport.reset(sendTransport);
			
			}

			{
				//createRecvTransprot
				request["id"] = msgId();
				request["method"] = "createWebRtcTransport";
				request["request"] = true;
				request["data"] = {
					{"forceTcp", false},
					{"producing", false},
					{"consuming", true},
				};
				_promise = std::unique_ptr<std::promise<std::string> >(new std::promise<std::string>);
				_websocket->send(request.dump());
				auto res = _promise->get_future().get();
				auto response = nlohmann::json::parse(res.begin(), res.end());
				m_recvTransportId = response["data"]["id"].get<std::string>();
				nlohmann::json iceParameters = response["data"]["iceParameters"];
				nlohmann::json iceCandidates = response["data"]["iceCandidates"];
				nlohmann::json dtlsParameters = response["data"]["dtlsParameters"];
				nlohmann::json sctpParameters = response["data"]["sctpParameters"];

				auto recvTransport = _mediasoupDevice->CreateRecvTransport(this, m_recvTransportId.c_str(), iceParameters, iceCandidates, dtlsParameters, sctpParameters, _peerConnectionOptions.get());
				_recvTransport.reset(recvTransport);
			}

			//join
			{
				request["id"] = msgId();
				request["method"] = "join";
				request["request"] = true;
				request["data"] = {
					{"displayName", "xlc"},
					{"device", "device"},
				};

				request["data"]["rtpCapabilities"] = _mediasoupDevice->GetRtpCapabilities();

				_promise = std::unique_ptr<std::promise<std::string> >(new std::promise<std::string>);
				_websocket->send(request.dump());
				std::string res = _promise->get_future().get();

				auto response = nlohmann::json::parse(res.begin(), res.end());

			}

			enableVideo();
			
		});

	}
	void MediaCtroller::onOpened()
	{
		startMediasoup();
	}
	void MediaCtroller::onClosed()
	{
	}
	void MediaCtroller::onFailed(int errorCode, const std::string& reason)
	{
	}
	void MediaCtroller::onMessage(const std::string& msg)
	{
		qDebug() << "onMessage:" << msg.c_str();
		auto notification = nlohmann::json::parse(msg.begin(), msg.end());
		if (notification.find("notification") != notification.end()){
			return;
		}
		else if (notification.find("request") != notification.end())
		{
			std::string method = notification["method"].get<std::string>();
			auto n = notification.dump();
			if ("newConsumer" == method){
				createNewConsumer(msg);
				nlohmann::json response = {
					{"response", true},
					{"ok", true},
					{"id", notification["id"]},
					{"data", nlohmann::json()},
				};
				_websocket->send(response.dump());
			}
	
			return;
		}
		try{
			_promise->set_value(msg);
		}
		catch (...){	
		}

	}
	std::future<std::string> MediaCtroller::OnProduce(mediasoupclient::SendTransport* transport, const std::string& kind, nlohmann::json rtpParameters, const nlohmann::json& appData)
	{
		nlohmann::json request;
		request["id"] = msgId();
		request["method"] = "produce";
		request["request"] = true;
		request["data"] = {
			{"transportId", transport->GetId()},
			{"kind", kind},
			{"rtpParameters", rtpParameters},
		};
		_promise = std::unique_ptr<std::promise<std::string> >(new std::promise<std::string>);
		_websocket->send(request.dump());
		return _promise->get_future();
	}
	std::future<std::string> MediaCtroller::OnProduceData(mediasoupclient::SendTransport* transport, const nlohmann::json& sctpStreamParameters, const std::string& label, const std::string& protocol, const nlohmann::json& appData)
	{
		return std::future<std::string>();
	}
	void MediaCtroller::OnTransportClose(mediasoupclient::Producer* producer)
	{
	}
	void MediaCtroller::OnTransportClose(mediasoupclient::Consumer* consumer)
	{
	}
	std::future<void> MediaCtroller::OnConnect(mediasoupclient::Transport* transport, const nlohmann::json& dtlsParameters)
	{
		//connectWebRtcTransport
		nlohmann::json request;
		request["id"] = msgId();
		request["method"] = "connectWebRtcTransport";
		request["request"] = true;
		request["data"] = {
			{"transportId", transport->GetId()},
			{"dtlsParameters", dtlsParameters}
		};
		//_promise = std::unique_ptr<std::promise<std::string> >(new std::promise<std::string>);
		_websocket->send(request.dump());
		qDebug() << "OnConnect" << request.dump().c_str();
		//std::string res = _promise->get_future().get();
		_promise2 = std::unique_ptr<std::promise<void> >(new std::promise<void>);
		_promise2->set_value();
		return _promise2->get_future();
	}
	void MediaCtroller::OnConnectionStateChange(mediasoupclient::Transport* transport, const std::string& connectionState)
	{
		qDebug() << "OnConnectionStateChange id:"<< transport->GetId().c_str() <<",state:"<< connectionState.c_str();
	}
}